<?php

namespace System\Enumerate;

use ReflectionClass;
use UnexpectedValueException;

/**
 * 枚举类
 * 
 * @package Dream
 * @subpackage Enum
 * @namespace System\Enumerate
 * @author lewis (lewisld@yeah.net)
 */
abstract class Enum
{

    /**
     * Constant with default value for creating enum object
     */
    const __default = NULL;

    private static $constants = [];

    private $value;

    private $strict;

    /**
     * Creates new enum object.
     * If child class overrides __construct(), it is required to call parent::__construct() in order for this class to work as expected.
     * 
     * @param mixed $initialValue Any value that is exists in defined constants
     * @param bool $strict If set to true, type and value must be equal
     * @throws UnexpectedValueException If value is not valid enum value
     */
    public function __construct($initialValue = NULL, $strict = TRUE)
    {
        
        $class = get_class($this);
        
        if (! array_key_exists($class, self::$constants)) {
            self::populateConstants();
        }
        
        if ($initialValue === NULL) {
            $initialValue = self::$constants[$class]["__default"];
        }
        $temp = self::$constants[$class];
        
        if (! in_array($initialValue, $temp, $strict)) {
            throw new UnexpectedValueException("Value is not in enum " . $class);
        }
        
        $this->value = $initialValue;
        $this->strict = $strict;
    }

    private function populateConstants()
    {
        
        $class = get_class($this);
        
        $r = new ReflectionClass($class);
        $constants = $r->getConstants();
        
        self::$constants = [
            $class => $constants
        ];
    }

    /**
     * Returns list of all defined constants in enum class.
     * Constants value are enum values.
     * 
     * @param bool $includeDefault If true, default value is included into return
     * @return array Array with constant values
     */
    public function getConstList($includeDefault = FALSE)
    {
        
        $class = get_class($this);
        
        if (! array_key_exists($class, self::$constants)) {
            self::populateConstants();
        }
        
        return $includeDefault ? array_merge(self::$constants[__CLASS__], [
            "__default" => self::__default
        ]) : self::$constants[__CLASS__];
    }

    /**
     * Returns string representation of an enum.
     * Defaults to value casted to string.
     * 
     * @return string String representation of this enum's value
     */
    public function __toString()
    {
        return $this->value;
    }

    /**
     * Checks if two enums are equal.
     * Only value is checked, not class type also. If enum was created with $strict = true, then strict comparison applies here also.
     * 
     * @param object $object representation of this enum's value
     * @return bool
     */
    public function equals($object)
    {
        if (! ($object instanceof Enum)) {
            return FALSE;
        }
        
        return $this->strict ? ($this->value === $object->value) : ($this->value == $object->value);
    }
}

